package paa.simulator.grafo;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import paa.simulator.grafo.PessoaPool.Pessoa;
import paa.simulator.grafo.VideoPool.Video;
import paa.simulator.grafo.simple.UndirectedSimpleGraph;

public class GraphBuilder {

	public enum Type {COMMON_FAVORITE_TAGS, COMMON_FAVORITE_VIDEO, COMMON_SUBSCRIPTIONS}
	
	/*
	public static void main(String[] args) {
		//p1
		String id1 = "jack";
		Subscriptions s1 = new Subscriptions(Arrays.asList("bozo", "john"));
		Uploads u1 = new Uploads(new HashMap<String, List<String>>());
		
		HashMap<String, List<String>> mf1 = new HashMap<String, List<String>>();
		mf1.put("v1", Arrays.asList("t1", "t2"));
		mf1.put("v2", Arrays.asList("t1", "t3"));
		mf1.put("v3", Arrays.asList("t2", "t4"));
		Favoritos f1 = new Favoritos(mf1);
		Pessoa p1 = new Pessoa(id1, f1, s1, u1);

		//p2
		String id2 = "bozo";
		Subscriptions s2 = new Subscriptions(new ArrayList<String>());
		HashMap<String, List<String>> um2 = new HashMap<String, List<String>>();
		um2.put("v5", Arrays.asList("t1", "t2"));
		
		Uploads u2 = new Uploads(um2);
		
		HashMap<String, List<String>> mf2 = new HashMap<String, List<String>>();
		mf2.put("v1", Arrays.asList("t1", "t2"));
		Favoritos f2 = new Favoritos(mf2);
		
		Pessoa p2 = new Pessoa(id2, f2, s2, u2);
		
		//p3
		String id3 = "john";
		Subscriptions s3 = new Subscriptions(Arrays.asList("bozo"));
		HashMap<String, List<String>> um3 = new HashMap<String, List<String>>();
		um3.put("v2", Arrays.asList("t1", "t3"));
		um3.put("v3", Arrays.asList("t2", "t4"));
		
		Uploads u3 = new Uploads(um3);
		
		HashMap<String, List<String>> mf3 = new HashMap<String, List<String>>();
		mf3.put("v1", Arrays.asList("t1", "t2"));
		Favoritos f3 = new Favoritos(mf3);
		
		Pessoa p3 = new Pessoa(id3, f3, s3, u3);

		//p4
		String id4 = "pessoa4";
		Subscriptions s4 = new Subscriptions(Arrays.asList("john"));
		Uploads u4 = new Uploads(new HashMap<String, List<String>>());
		
		HashMap<String, List<String>> mf4 = new HashMap<String, List<String>>();
		Favoritos f4 = new Favoritos(mf4);
		
		Pessoa p4 = new Pessoa(id4, f4, s4, u4);
		
		//teste
		
		List<Pessoa> pessoas = Arrays.asList(p1, p2, p3, p4);
		
		//algoritmo gerado de grafo.
		Graph graph = criaGrafo(pessoas, 0);
		System.out.println(graph.edgeSet());
	}
	*/
	
	public static UndirectedSimpleGraph<PessoaVertex> criaGrafo(List<Pessoa> pessoas, double threshold, PossibleRelations possibleRelations, Type type) {
		UndirectedSimpleGraph<PessoaVertex> grafo = new UndirectedSimpleGraph<PessoaVertex>();
		
		Iterator<Pessoa> it = pessoas.iterator();
		Set<Pessoa> alreadyChecked = new HashSet<Pessoa>();
		int i = 0;
		while(it.hasNext()) {
			
			Pessoa p = it.next();
			alreadyChecked.clear();
			
			PessoaVertex pv = new PessoaVertex(p.getID());
			if (!grafo.containsVertex(pv)) {
				grafo.addVertex(pv);
			}
			
			switch (type) {
			case COMMON_FAVORITE_TAGS:
				buildRelationsFavoriteTags(threshold, possibleRelations, grafo, alreadyChecked, p, pv);
				break;
			case COMMON_FAVORITE_VIDEO:
				buildRelationsFavoriteVideos(threshold, possibleRelations, grafo, alreadyChecked, p, pv);
				break;
			case COMMON_SUBSCRIPTIONS:
				buildRelationsSubscriptions(threshold, possibleRelations, grafo, alreadyChecked, p, pv);
				break;
			}

			i++;
			if (i % 100 == 0) {
				System.out.println(i);
			}
			it.remove();
		}
		
		return grafo;
	}

	private static void buildRelationsFavoriteTags(double threshold, PossibleRelations possibleRelations, UndirectedSimpleGraph<PessoaVertex> grafo, Set<Pessoa> alreadyChecked, Pessoa p, PessoaVertex pv) {
		Favoritos favoritos = p.getFavoritos();
		Set<Integer> tagsFavP = favoritos.getTags();
		for (Integer tag : tagsFavP) {
			Set<Pessoa> possibleTagRelations = possibleRelations.getPossibleTagRelations(tag);
			if (possibleTagRelations != null) {
				possibleRelations.removePossibleTagRelation(tag, p);

				for (Pessoa op : possibleTagRelations) {
					if (!alreadyChecked.contains(op)) {
						Set<Integer> tagsFavOP = op.getFavoritos().getTags();
						
						int interSize = intersection(tagsFavP, tagsFavOP);
						int unionSize = union(tagsFavP, tagsFavOP);
						
						PessoaVertex opv = new PessoaVertex(op.getID());
						if (!grafo.containsVertex(opv)) {
							grafo.addVertex(opv);
						}
						
						double similaridade = (double)interSize / unionSize;
						if (similaridade > threshold) {
							grafo.addEdge(pv, opv);
						}
						
						alreadyChecked.add(op);
					}
				}
			}
		}
	}
	
	private static void buildRelationsFavoriteVideos(double threshold, PossibleRelations possibleRelations, UndirectedSimpleGraph<PessoaVertex> grafo, Set<Pessoa> alreadyChecked, Pessoa p, PessoaVertex pv) {
		Favoritos favoritos = p.getFavoritos();
		Set<Video> videosFavP = favoritos.getFavoritos();
		
		for (Video video : videosFavP) {
			Set<Pessoa> possibleVideoRelations = possibleRelations.getPossibleVideoRelations(video);
			if (possibleVideoRelations != null) {
				possibleRelations.removePossibleVideoRelation(video, p);

				for (Pessoa op : possibleVideoRelations) {
					if (!alreadyChecked.contains(op)) {
						Set<Video> videosFavOP = op.getFavoritos().getFavoritos();
						
						int interSize = intersection(videosFavP, videosFavOP);
						int unionSize = union(videosFavP, videosFavOP);
						
						PessoaVertex opv = new PessoaVertex(op.getID());
						if (!grafo.containsVertex(opv)) {
							grafo.addVertex(opv);
						}
						
						double similaridade = (double)interSize / unionSize;
						if (similaridade > threshold) {
							grafo.addEdge(pv, opv);
						}
						
						alreadyChecked.add(op);
					}
				}
			}
		}
	}
	
	private static void buildRelationsSubscriptions(double threshold, PossibleRelations possibleRelations, UndirectedSimpleGraph<PessoaVertex> grafo, Set<Pessoa> alreadyChecked, Pessoa p, PessoaVertex pv) {
		Subscriptions subscriptions = p.getSubscriptions();
		Set<Pessoa> subsP = subscriptions.getUsuarios();
		
		for (Pessoa sub : subsP) {
			Set<Pessoa> possibleSubsRelations = possibleRelations.getPossibleSubscriptionRelations(sub);
			
			if (possibleSubsRelations != null) {
				possibleRelations.removePossibleSubscriptionRelation(sub, p);

				for (Pessoa op : possibleSubsRelations) {
					if (!alreadyChecked.contains(op)) {
						Set<Pessoa> subsOP = op.getSubscriptions().getUsuarios();
						
						int interSize = intersection(subsP, subsOP);
						int unionSize = union(subsP, subsOP);
						
						PessoaVertex opv = new PessoaVertex(op.getID());
						if (!grafo.containsVertex(opv)) {
							grafo.addVertex(opv);
						}
						
						double similaridade = (double)interSize / unionSize;
						if (similaridade > threshold) {
							grafo.addEdge(pv, opv);
						}
						
						alreadyChecked.add(op);
					}
				}
			}
		}
	}
	
	public static <T extends Object> int union(Set<T> tags1, Set<T> tags2) {
		int returnValue = tags2.size();
		
		for (T t : tags1) {
			if (!tags2.contains(t)) {
				returnValue++;
			}
		}
		
		return returnValue;
	}
	
	public static <T extends Object> int intersection(Set<T> tags1, Set<T> tags2) {
		int returnValue = 0;
		
		for(T t : tags1) {
			if (tags2.contains(t)) {
				returnValue++;
			}
		}
		
		return returnValue;
	}
}
